#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include <psp2/kernel/processmgr.h>
#include <psp2/display.h>
#include <psp2/gxm.h>

#include <vita2d.h>
#include "vita2d_ext.h"

#include "video.h"
#include "utils.h"
#include "ui.h"
#include "menu.h"
#include "config.h"
#include "shaders.h"
#include "retro.h"
#include "file.h"
#include "boot.h"

#include "libretro.h"

static vita2d_texture *video_tex = NULL;
static vita2d_texture *overlay_tex = NULL;
static vita2d_shader *video_shader = NULL;
enum SceGxmTextureFormat video_texture_format = SCE_GXM_TEXTURE_FORMAT_U5U6U5_RGB;
enum SceGxmTextureFormat old_video_texture_format = SCE_GXM_TEXTURE_FORMAT_U5U6U5_RGB;
static float video_scale_x = 1.0f, video_scale_y = 1.0f;
static float rotate_rad = 0.0f;
static unsigned int frame_width = 0, frame_height = 0;

static int video_ready = 0, video_pause = 1;
static double video_fps = 0;
static float video_fps_scale = 1.0f;
static int old_screen_size_config = -1, old_show_overlay_config = -1, old_display_rotate_config = -1;
static int video_need_refresh = 1;

static uint64_t cur_micros = 0, old_micros = 0, last_micros = 0, delta_micros = 0, delay_micros = 0;
static uint32_t frames = 0;
static float fps = 0.0f;

char *overlay_names[] = {
    "1x.png",
    "2x.png",
    "3x.png",
    "fit.png",
    "fit_8-7.png",
    "fit_4-3.png",
    "fit_3-2.png",
    "full.png",
};

char *overlay_rotate_names[] = {
    "1x_r.png",
    "2x_r.png",
    "3x_r.png",
    "fit_r.png",
    "fit_8-7_r.png",
    "fit_4-3_r.png",
    "fit_3-2_r.png",
    "full_r.png",
};

int vitaVideoIsVideoNeedRefresh()
{
    return video_need_refresh;
}

void vitaVideoSetVideoNeedRefresh(int enable)
{
    video_need_refresh = enable;
}

float vitaVideoGetFpsScale()
{
    return video_fps_scale;
}

void vitaVideoSetFpsScale(float scale)
{
    video_fps_scale = scale;
    video_fps = (double)core_system_av_info.timing.fps * scale;
    delay_micros = 1000000.0f / video_fps;
}

unsigned int vitaVideoGetFrameWidth()
{
    return frame_width;
}

unsigned int vitaVideoGetFrameHeight()
{
    return frame_height;
}

vita2d_shader *vitaVideoGetVideoShader()
{
    return video_shader;
}

vita2d_texture *vitaVideoGetVideoTexture()
{
    return video_tex;
}

void *vitaVideoGetVideoTextureDatap()
{
    return vita2d_texture_get_datap(video_tex);
}

unsigned int vitaVideoGetVideoTextureWidth()
{
    return vita2d_texture_get_width(video_tex);
}

unsigned int vitaVideoGetVideoTextureHeight()
{
    return vita2d_texture_get_height(video_tex);
}

unsigned int vitaVideoGetVideoTextureStride()
{
    return vita2d_texture_get_stride(video_tex);
}

unsigned int vitaVideoGetVideoTextureBytespp()
{
    if (video_texture_format == SCE_GXM_TEXTURE_FORMAT_X1U5U5U5_1RGB || video_texture_format == SCE_GXM_TEXTURE_FORMAT_U5U6U5_RGB)
        return 2;
    else if (video_texture_format == SCE_GXM_TEXTURE_FORMAT_X8U8U8U8_1RGB)
        return 4;

    return 0;
}

float vitaVideoGetVideoRotateRad()
{
    return rotate_rad;
}

void vitaVideoMakeBaseWH(uint32_t *base_width, uint32_t *base_height)
{
    float aspect_ratio = core_system_av_info.geometry.aspect_ratio;
    uint32_t height = core_system_av_info.geometry.base_height;
    uint32_t width = core_system_av_info.geometry.base_width;
    if (aspect_ratio > 0)
    {
        width = height * aspect_ratio;
        if (width < core_system_av_info.geometry.base_width)
        {
            width = core_system_av_info.geometry.base_width;
            height = width / aspect_ratio;
        }
    }

    *base_width = width;
    *base_height = height;
}

uint32_t *vitaVideoGetScreenshot(uint32_t *new_width, uint32_t *new_height, uint64_t *new_size)
{
    uint32_t conver_width = *new_width;
    uint32_t conver_height = *new_height;
    float aspect_ratio = (float)conver_width / (float)conver_height;

    if (conver_width > SCREEN_WIDTH)
    {
        conver_width = SCREEN_WIDTH;
        conver_height = conver_width / aspect_ratio;
    }
    if (conver_height > SCREEN_HEIGHT)
    {
        conver_height = SCREEN_HEIGHT;
        conver_width = conver_height * aspect_ratio;
    }
    conver_width = (conver_width + 7) & ~7;
    if (conver_width != *new_width)
        conver_height = conver_width / aspect_ratio;

    float scale_x, scale_y;
    if (graphics_config.display_rotate == 1 || graphics_config.display_rotate == 3)
    {
        scale_x = (float)conver_height / (float)frame_width;
        scale_y = (float)conver_width / (float)frame_height;
    }
    else
    {
        scale_x = (float)conver_width / (float)frame_width;
        scale_y = (float)conver_height / (float)frame_height;
    }
    vita2d_texture *rt_texture = vita2d_create_empty_texture_rendertarget(SCREEN_WIDTH, SCREEN_HEIGHT, SCE_GXM_TEXTURE_FORMAT_A8B8G8R8);
    if (!rt_texture)
        return NULL;

    vita2d_wait_rendering_done();
    vita2d_pool_reset();
    vita2d_start_drawing_advanced(rt_texture, 0);
    if (video_shader)
        vita2d_draw_texture_part_scale_rotate_shader(video_tex, video_shader, conver_width / 2, conver_height / 2, 0, 0,
                                                     frame_width, frame_height, scale_x, scale_y, rotate_rad);
    else
        vita2d_draw_texture_part_scale_rotate(video_tex, conver_width / 2, conver_height / 2, 0, 0,
                                              frame_width, frame_height, scale_x, scale_y, rotate_rad);
    vita2d_end_drawing();
    vita2d_wait_rendering_done();

    uint64_t conver_size = conver_width * conver_height * 4;
    uint32_t *conver_buf = (uint32_t *)malloc(conver_size);
    uint8_t *conver_datap = (uint8_t *)conver_buf;
    uint32_t conver_stride = conver_width * 4;

    uint8_t *rt_tex_datap = (uint8_t *)vita2d_texture_get_datap(rt_texture);
    uint32_t rt_tex_stride = vita2d_texture_get_stride(rt_texture);

    int i;
    for (i = 0; i < conver_height; i++, rt_tex_datap += rt_tex_stride, conver_datap += conver_stride)
        memcpy(conver_datap, rt_tex_datap, conver_stride);

    vita2d_free_texture(rt_texture);

    *new_width = conver_width;
    *new_height = conver_height;
    *new_size = conver_size;

    return conver_buf;
}

void vitaVideoPause()
{
    video_pause = 1;
}

void vitaVideoResume()
{
    video_pause = 0;
}

int vitaVideoRefresh()
{
    if (!video_ready)
        return -1;

    if (old_video_texture_format != video_texture_format)
    {
        if (vitaVideoCreatVideoTexture() < 0)
            return -1;
    }

    if (old_screen_size_config != graphics_config.screen_size ||
        old_show_overlay_config != graphics_config.show_overlay ||
        old_display_rotate_config != graphics_config.display_rotate)
    {
        old_screen_size_config = graphics_config.screen_size;
        old_show_overlay_config = graphics_config.show_overlay;
        old_display_rotate_config = graphics_config.display_rotate;

        if (overlay_tex)
        {
            vita2d_wait_rendering_done();
            vita2d_free_texture(overlay_tex);
            overlay_tex = NULL;
        }
        if (graphics_config.show_overlay)
        {
            char **names;
            if (graphics_config.display_rotate == 1 || graphics_config.display_rotate == 3)
                names = overlay_rotate_names;
            else
                names = overlay_names;

            char *name = names[graphics_config.screen_size];
            char path[MAX_PATH_LENGTH];
            if (private_assets_dir)
            {
                snprintf(path, MAX_PATH_LENGTH, "%s/%s/%s", private_assets_dir, OVERLAYS_DIR_NAME, name);
                overlay_tex = vita2d_load_PNG_file(path);
            }
            if (!overlay_tex)
            {
                snprintf(path, MAX_PATH_LENGTH, "%s/%s/%s", public_assets_dir, OVERLAYS_DIR_NAME, name);
                overlay_tex = vita2d_load_PNG_file(path);
            }
        }
    }

    if (graphics_config.display_rotate == 1)
        rotate_rad = M_PI_2;
    else if (graphics_config.display_rotate == 2)
        rotate_rad = M_PI_2 * 2;
    else if (graphics_config.display_rotate == 3)
        rotate_rad = M_PI_2 * 3;
    else
        rotate_rad = 0.0f;

    uint32_t tmp_base_width, tmp_base_height;
    vitaVideoMakeBaseWH(&tmp_base_width, &tmp_base_height);

    float base_width, base_height;
    if (graphics_config.display_rotate == 1 || graphics_config.display_rotate == 3)
    {
        base_width = (float)tmp_base_height;
        base_height = (float)tmp_base_width;
    }
    else
    {
        base_width = (float)tmp_base_width;
        base_height = (float)tmp_base_height;
    }

    float base_aspect_ratio = base_width / base_height;

    float width, height;

    if (graphics_config.screen_size == EMU_SCREEN_SIZE_2X)
    { // 2倍大小
        width = base_width * 2;
        height = base_height * 2;
    }
    else if (graphics_config.screen_size == EMU_SCREEN_SIZE_3X)
    { // 3倍大小
        width = base_width * 3;
        height = base_height * 3;
    }
    else if (graphics_config.screen_size == EMU_SCREEN_SIZE_FIT)
    { // 等比铺满
        height = SCREEN_HEIGHT;
        width = height * base_aspect_ratio;
    }
    else if (graphics_config.screen_size == EMU_SCREEN_SIZE_FIT_8_7)
    { // 8:7铺满
        height = SCREEN_HEIGHT;
        width = height * 8 / 7;
    }
    else if (graphics_config.screen_size == EMU_SCREEN_SIZE_FIT_4_3)
    { // 4:3铺满
        height = SCREEN_HEIGHT;
        width = height * 4 / 3;
    }
    else if (graphics_config.screen_size == EMU_SCREEN_SIZE_FIT_3_2)
    { // 3:2铺满
        height = SCREEN_HEIGHT;
        width = height * 3 / 2;
    }
    else if (graphics_config.screen_size == EMU_SCREEN_SIZE_FULL)
    { // 拉伸铺满
        width = SCREEN_WIDTH;
        height = SCREEN_HEIGHT;
    }
    else
    { // 1倍大小
        width = base_width;
        height = base_height;
    }

    float aspect_ratio = width / height;

    if (width > SCREEN_WIDTH)
    {
        width = SCREEN_WIDTH;
        height = width / aspect_ratio;
    }
    if (height > SCREEN_HEIGHT)
    {
        height = SCREEN_HEIGHT;
        width = height * aspect_ratio;
    }

    if (graphics_config.display_rotate == 1 || graphics_config.display_rotate == 3)
    {
        video_scale_x = height / frame_width;
        video_scale_y = width / frame_height;
    }
    else
    {
        video_scale_x = width / frame_width;
        video_scale_y = height / frame_height;
    }

    if (graphics_config.graphics_filtering == 1)
        video_shader = lcd3x_shader;
    else if (graphics_config.graphics_filtering == 2)
        video_shader = sharp_bilinear_simple_shader;
    else if (graphics_config.graphics_filtering == 3)
        video_shader = sharp_bilinear_shader;
    else if (graphics_config.graphics_filtering == 4)
        video_shader = advanced_aa_shader;
    else
        video_shader = NULL;

    if (graphics_config.smooth_graphics)
        vita2d_texture_set_filters(video_tex, SCE_GXM_TEXTURE_FILTER_LINEAR, SCE_GXM_TEXTURE_FILTER_LINEAR);
    else
        vita2d_texture_set_filters(video_tex, SCE_GXM_TEXTURE_FILTER_POINT, SCE_GXM_TEXTURE_FILTER_POINT);

    return 0;
}

void vitaVideoDrawFrame()
{
    if (!video_ready)
        return;

    if (video_need_refresh)
    {
        vitaVideoRefresh();
        video_need_refresh = 0;
    }

    if (overlay_tex && graphics_config.show_overlay == 2)
        vita2d_draw_texture(overlay_tex, 0.0f, 0.0f);

    if (video_shader)
        vita2d_draw_texture_part_scale_rotate_shader(video_tex, video_shader, SCREEN_HALF_WIDTH, SCREEN_HALF_HEIGHT, 0, 0, frame_width, frame_height, video_scale_x, video_scale_y, rotate_rad);
    else
        vita2d_draw_texture_part_scale_rotate(video_tex, SCREEN_HALF_WIDTH, SCREEN_HALF_HEIGHT, 0, 0, frame_width, frame_height, video_scale_x, video_scale_y, rotate_rad);

    if (overlay_tex && graphics_config.show_overlay == 1)
        vita2d_draw_texture(overlay_tex, 0.0f, 0.0f);
}

void vitaVideoDraw()
{
    vita2d_start_drawing();
    vita2d_clear_screen();

    vitaVideoDrawFrame();

    if (graphics_config.show_fps)
    {
        UiDrawTextf(0.0f, 0.0f, WHITE, "FPS: %.2f", fps);
        cur_micros = sceKernelGetProcessTimeWide();
        if (cur_micros >= (last_micros + 1000000))
        {
            delta_micros = cur_micros - last_micros;
            last_micros = cur_micros;
            fps = (frames / (double)delta_micros) * 1000000.0f;
            frames = 0;
        }
        frames++;
    }

    if (video_fps_scale != 1.0f)
    {
        char fps_scale_string[12];
        snprintf(fps_scale_string, 12, "%.2fX", video_fps_scale);
        UiDrawTextf(SCREEN_WIDTH - UiGetTextWidth(fps_scale_string), 0.0f, WHITE, "%.2fX", video_fps_scale);
    }

    vita2d_end_drawing();

    cur_micros = sceKernelGetProcessTimeWide();
    delta_micros = cur_micros - old_micros;
    if (delta_micros < delay_micros)
    {
        sceKernelDelayThread(delay_micros - delta_micros);
        old_micros += delay_micros;
    }
    else
    {
        old_micros = cur_micros;
    }

    vita2d_swap_buffers();
}

void retroVideoRefreshCallback(const void *data, unsigned width, unsigned height, size_t pitch)
{
    if (!video_ready)
        return;

    if (data && pitch > 0)
    {
        if ((frame_width != width) || (frame_height != height))
        {
            frame_width = width;
            frame_height = height;
            video_need_refresh = 1;
        }

        const uint8_t *in_buf = (const uint8_t *)data;
        uint8_t *out_buf = (uint8_t *)vita2d_texture_get_datap(video_tex);
        unsigned int in_pitch = pitch;
        unsigned int out_pitch = vita2d_texture_get_stride(video_tex);
        int i;
        for (i = 0; i < height; i++, in_buf += in_pitch, out_buf += out_pitch)
        {
            memcpy(out_buf, in_buf, in_pitch);
        }
    }

    if (!video_pause)
        vitaVideoDraw();
}

int vitaVideoCreatVideoTexture()
{
    int pause = video_pause;
    int ready = video_ready;

    video_pause = 1;
    video_ready = 0;

    if (video_tex)
    {
        vita2d_wait_rendering_done();
        vita2d_free_texture(video_tex);
    }
    video_tex = vita2d_create_empty_texture_format(core_system_av_info.geometry.max_width, core_system_av_info.geometry.max_height, video_texture_format);
    if (!video_tex)
    {
        APP_LOG("create video_tex failed\n");
        return -1;
    }

    video_ready = ready;
    video_pause = pause;
    old_video_texture_format = video_texture_format;

    return 0;
}

int vitaVideoInit()
{
    APP_LOG("vitaVideoInit...\n");

    video_ready = 0;
    video_pause = 1;

    if (vitaVideoCreatVideoTexture() < 0)
        return -1;

    frame_width = core_system_av_info.geometry.base_width;
    frame_height = core_system_av_info.geometry.base_height;
    video_fps = (double)core_system_av_info.timing.fps;
    video_fps_scale = 1.0f;
    delay_micros = 1000000.0f / video_fps;
    old_screen_size_config = -1;
    old_show_overlay_config = -1;
    old_display_rotate_config = -1;
    video_need_refresh = 1;
    video_ready = 1;

    APP_LOG("max_width: %d, max_height: %d\n", core_system_av_info.geometry.max_width, core_system_av_info.geometry.max_height);
    APP_LOG("base_width: %d, base_height: %d\n", core_system_av_info.geometry.base_width, core_system_av_info.geometry.base_height);
    APP_LOG("fps: %.2f\n", core_system_av_info.timing.fps);
    APP_LOG("vitaVideoInit done\n");

    return 0;
}

int vitaVideoShutdown()
{
    APP_LOG("vitaVideoShutdown...\n");

    video_ready = 0;
    video_pause = 1;

    if (video_tex)
    {
        vita2d_wait_rendering_done();
        vita2d_free_texture(video_tex);
        video_tex = NULL;
    }
    if (overlay_tex)
    {
        vita2d_wait_rendering_done();
        vita2d_free_texture(overlay_tex);
        overlay_tex = NULL;
    }

    APP_LOG("vitaVideoShutdown done\n");

    return 0;
}
